﻿using System;
using System.Collections.Generic;
using System.Data;

    /// <summary>
    /// 
    /// </summary>
    public class OptionCalculator
    {
        private List<OptionData> optionDatas = new List<OptionData>();
        private OptionSettings _option = new OptionSettings();
        private double _atm = 0;

        /// <summary>
        /// Option Settings
        /// </summary>
        public OptionSettings OptionSettings
        {
            get { return _option; }
            set { _option = value; }
        }

        #region constructor
        /// <summary>
        /// initialize OptionCalculator
        /// </summary>
        public OptionCalculator()
        {
        }

        /// <summary>
        /// initialize OptionCalculator
        /// </summary>
        public OptionCalculator(OptionSettings option, decimal ask, decimal bid)
        {
            _option = option;
            _atm = GetAtm(ask, bid, option.StrikeIncrement);
        }

        #endregion

        #region public

        #region UseForWeb

        /// <summary>
        /// Get SkewSettings for OptionPricing Page
        /// </summary>
        public DataTable GetSkewSettings()
        {
            DataTable dtSkew = new DataTable("SkewSettings");
            string[] columns = { "Strike", "Volatility", "BidCall", "AskCall", "BidPut", "AskPut" };
            foreach (var item in columns)
            {
                DataColumn dc = new DataColumn(item);
                dtSkew.Columns.Add(dc);
            }

            for (int i = 0; i < _option.tradableCount * 2 + 1; i++)
            {
                int index = i - _option.tradableCount;
                double strike = _atm + index * _option.StrikeIncrement;
                double time = (_option.Expiry - DateTime.Now).TotalDays;
                double volatility = 0;
                if (index > 0)
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.UpperSlope, _option.UpperCurve, strike, _atm, _option.CruxFactor);
                }
                else
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.LowerSlope, _option.LowerCurve, strike, _atm, _option.CruxFactor);
                }
                double askCall = Math.Round(GetAsk(OptionType.Call, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor), 4);
                double bidCall = Math.Round(GetBid(OptionType.Call, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor), 4);
                double askPut = Math.Round(GetAsk(OptionType.Put, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor), 4);
                double bidPut = Math.Round(GetBid(OptionType.Put, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor), 4);

                DataRow dr = dtSkew.NewRow();
                dr["Strike"] = strike;
                dr["Volatility"] = Math.Round(volatility, 4).ToString("P");
                dr["BidCall"] = bidCall.ToString("#0.0000");
                dr["AskCall"] = askCall.ToString("#0.0000");
                dr["BidPut"] = bidPut.ToString("#0.0000");
                dr["AskPut"] = askPut.ToString("#0.0000");
                dtSkew.Rows.Add(dr);
            }
            return dtSkew;
        }

        /// <summary>
        /// get spread list
        /// </summary>
        public List<double> GetSpread(OptionType optionType)
        {
            List<double> listSpread = new List<double>();

            for (int i = 0; i < _option.tradableCount * 2 + 1; i++)
            {
                int index = i - _option.tradableCount;
                double strike = _atm + index * _option.StrikeIncrement;
                double time = (_option.Expiry - DateTime.Now).TotalDays;
                double volatility = 0;
                if (index > 0)
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.UpperSlope, _option.UpperCurve, strike, _atm, _option.CruxFactor);
                }
                else
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.LowerSlope, _option.LowerCurve, strike, _atm, _option.CruxFactor);
                }
                double ask = GetAsk(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor);
                double bid = GetBid(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor);
                double spread = ask - bid;
                listSpread.Add(spread);
            }

            return listSpread;
        }

        /// <summary>
        /// get spread change list
        /// </summary>
        public List<double> GetSpreadChange(OptionType optionType)
        {
            List<double> listSpreadChange = new List<double>();
            List<double> listSpread = GetSpread(optionType);
            for (int i = 0; i < listSpread.Count - 1; i++)
            {
                double spreadChange = Math.Abs(listSpread[i + 1] - listSpread[i]);
                listSpreadChange.Add(spreadChange);
            }
            return listSpreadChange;
        }

        /// <summary>
        /// get list ask price
        /// </summary>
        public List<double> GetAsk(OptionType optionType)
        {
            List<double> listAsk = new List<double>();

            for (int i = 0; i < _option.tradableCount * 2 + 1; i++)
            {
                int index = i - _option.tradableCount;
                double strike = _atm + index * _option.StrikeIncrement;
                double time = (_option.Expiry - DateTime.Now).TotalDays;
                double volatility = 0;
                if (index > 0)
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.UpperSlope, _option.UpperCurve, strike, _atm, _option.CruxFactor);
                }
                else
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.LowerSlope, _option.LowerCurve, strike, _atm, _option.CruxFactor);
                }
                double ask = Math.Round(GetAsk(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor), 4);
                listAsk.Add(ask);
            }

            return listAsk;
        }

        /// <summary>
        /// get list bid price
        /// </summary>
        public List<double> GetBid(OptionType optionType)
        {
            List<double> listBid = new List<double>();

            for (int i = 0; i < _option.tradableCount * 2 + 1; i++)
            {
                int index = i - _option.tradableCount;
                double strike = _atm + index * _option.StrikeIncrement;
                double time = (_option.Expiry - DateTime.Now).TotalDays;
                double volatility = 0;
                if (index > 0)
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.UpperSlope, _option.UpperCurve, strike, _atm, _option.CruxFactor);
                }
                else
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.LowerSlope, _option.LowerCurve, strike, _atm, _option.CruxFactor);
                }
                double bid = Math.Round(GetBid(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor), 4);
                listBid.Add(bid);
            }

            return listBid;
        }

        /// <summary>
        /// get volatility list
        /// </summary>
        /// <returns>volatility list</returns>
        public List<double> GetVolatilities()
        {
            List<double> volatilities = new List<double>();

            var interval = (int)Math.Ceiling((double)_option.tradableCount / 10);
            var count = (int)Math.Ceiling(_option.tradableCount / Math.Ceiling((double)_option.tradableCount / 10));

            for (int i = 0; i < count * 2 + 1; i++)
            {
                int index = 0;
                if (i == 0)
                    index = -_option.tradableCount;
                else if (i == count * 2)
                    index = _option.tradableCount;
                else
                    index = (i - count) * interval;
                //for (int i = 0; i < _option.tradableCount * 2 + 1; i++)
                //{
                //    int index = i - _option.tradableCount;
                double strike = _atm + index * _option.StrikeIncrement;
                double volatility = 0;
                if (index > 0)
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.UpperSlope, _option.UpperCurve, strike, _atm, _option.CruxFactor);
                }
                else
                {
                    volatility = GetVolatility(_option.AtmVolatility, _option.LowerSlope, _option.LowerCurve, strike, _atm, _option.CruxFactor);
                }

                volatilities.Add(volatility);
            }
            return volatilities;

        }

        /// <summary>
        /// calculate atm price
        /// </summary>
        public static double GetAtm(decimal ask, decimal bid, double increment)
        {
            double spotPrice = Convert.ToDouble((ask + bid) / 2);
            double atm = ((int)(spotPrice / increment)) * increment; ;
            return atm;

        }

        /// <summary>
        /// Gets the strike price.
        /// </summary>
        /// <param name="strikeIncrement">The strike increment.</param>
        /// <param name="ask">The ask.</param>
        /// <param name="bid">The bid.</param>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        public static double GetStrikePrice(double strikeIncrement, decimal ask, decimal bid, int index)
        {
            var atm = GetAtm(ask, bid, strikeIncrement);
            double strike = atm + index * strikeIncrement;
            return strike;
        }

        #endregion

        #region UseForClient

        /// <summary>
        /// get option list
        /// </summary>
        /// <param name="optionType">Type of the option.</param>
        /// <param name="nonTradableCount">The non tradable count.</param>
        /// <param name="serverClientSpan">The server client span.</param>
        /// <returns>
        /// option data list
        /// </returns>
        public List<OptionData> GetOptionList(OptionType optionType, int nonTradableCount, TimeSpan serverClientSpan)
        {
            for (int i = 0; i < _option.tradableCount * 2 + 1; i++)
            {
                int index = i - _option.tradableCount;
                if (Math.Abs(index) > nonTradableCount)
                {
                    double strike = _atm + index * _option.StrikeIncrement;

                    OptionData optionData = new OptionData();
                    double time = DateTime.Compare(_option.Expiry, DateTime.Now + serverClientSpan) < 0 ? 0 : (_option.Expiry - DateTime.Now - serverClientSpan).TotalDays;

                    double volatility = 0;
                    if (index > 0)
                    {
                        volatility = GetVolatility(_option.AtmVolatility, _option.UpperSlope, _option.UpperCurve, strike, _atm, _option.CruxFactor);
                    }
                    else
                    {
                        volatility = GetVolatility(_option.AtmVolatility, _option.LowerSlope, _option.LowerCurve, strike, _atm, _option.CruxFactor);
                    }

                    double gamma = GetGamma(_atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate);
                    double vega = GetVega(_atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate);

                    FillOptionData(optionType, strike, time, volatility, gamma, optionData, vega);
                    optionDatas.Add(optionData);
                }
            }
            return optionDatas;
        }

        /// <summary>
        /// get price by strike
        /// </summary>
        /// <param name="optionType">option type : call or put</param>
        /// <param name="settings">option settings</param>
        /// <param name="strike">strike price</param>
        /// <param name="ask">spot ask</param>
        /// <param name="bid">spot bid</param>
        /// <param name="side">side ; buy or sell</param>
        /// <param name="serverclientSpan">The serverclient span.</param>
        /// <returns>
        /// option price :ask or bid
        /// </returns>
        public static double GetPriceByStrike(OptionType optionType, OptionSettings settings, double strike, decimal ask, decimal bid, Side side, TimeSpan serverclientSpan)
        {
            var atm = GetAtm(ask, bid, settings.StrikeIncrement);
            double time = DateTime.Compare(settings.Expiry, DateTime.Now + serverclientSpan) < 0 ? 0 : (settings.Expiry - DateTime.Now - serverclientSpan).TotalDays;
            double volatility = 0;

            if (strike - atm >= 0)
            {
                volatility = GetVolatility(settings.AtmVolatility, settings.UpperSlope, settings.UpperCurve, strike, atm, settings.CruxFactor);
            }
            else
            {
                volatility = GetVolatility(settings.AtmVolatility, settings.LowerSlope, settings.LowerCurve, strike, atm, settings.CruxFactor);
            }

            if (side == Side.Buy)
            {
                return GetAsk(optionType, atm, strike, settings.InterestRate, time, volatility, settings.ForeignRate, settings.SpreadFactor);
            }
            else
            {
                return GetBid(optionType, atm, strike, settings.InterestRate, time, volatility, settings.ForeignRate, settings.SpreadFactor);
            }
        }

        /// <summary>
        /// Options the un expired.
        /// </summary>
        /// <param name="optioninfo">The optioninfo.</param>
        /// <param name="serverClientSpan">The server client span.</param>
        /// <returns></returns>
        public bool GetOptionUnExpired(OptionInfo optioninfo, TimeSpan serverClientSpan)
        {
            return GetDateList(optioninfo.CreationDate, serverClientSpan, optioninfo.ExpiryMonths, optioninfo.ExpiryWeek, optioninfo.ExpiryDays,
                optioninfo.ExpiryTime.Hour, optioninfo.ExpiryTime.Minute, optioninfo.TradingPeriod).Count > 0;
        }

        /// <summary>
        /// Options the un expired.
        /// </summary>
        /// <param name="beginDatetime">The begin datetime.</param>
        /// <param name="serverClientSpan">The server client span.</param>
        /// <param name="monthNum">The month num.</param>
        /// <param name="weekNum">The week num.</param>
        /// <param name="weekDay">The week day.</param>
        /// <param name="hour">The hour.</param>
        /// <param name="minute">The minute.</param>
        /// <param name="tradingPeriod">The trading period.</param>
        /// <returns></returns>
        public bool GetOptionUnExpired(DateTime beginDatetime, TimeSpan serverClientSpan, int monthNum, int weekNum, int weekDay, int hour, int minute, int tradingPeriod)
        {
            return GetDateList(beginDatetime, serverClientSpan, monthNum, weekNum, weekDay, hour, minute, tradingPeriod).Count > 0;
        }


        #endregion

        #region UseForServer

        /// <summary>
        /// 
        /// </summary>
        /// <param name="optionType"></param>
        /// <param name="settings"></param>
        /// <param name="ask"></param>
        /// <param name="bid"></param>
        /// <param name="strike"></param>
        /// <returns></returns>
        public static double GetAskPrice(OptionType optionType, OptionSettings settings, decimal ask, decimal bid, decimal strike)
        {
            return GetPriceByStrike(optionType, settings, (double)strike, ask, bid, Side.Buy, new TimeSpan(0));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="optionType"></param>
        /// <param name="settings"></param>
        /// <param name="ask"></param>
        /// <param name="bid"></param>
        /// <param name="strike"></param>
        /// <returns></returns>
        public static double GetBidPrice(OptionType optionType, OptionSettings settings, decimal ask, decimal bid, decimal strike)
        {
            return GetPriceByStrike(optionType, settings, (double)strike, ask, bid, Side.Sell, new TimeSpan(0));
        }

        #endregion

        #region UseForAll

        /// <summary>
        /// Gets the expiry list.
        /// </summary>
        /// <param name="optioninfo">The optioninfo.</param>
        /// <param name="serverClientSpan">The server client span.</param>
        /// <returns></returns>
        public List<DateTime> GetExpiryList(OptionInfo optioninfo, TimeSpan serverClientSpan)
        {
            return GetDateList(optioninfo.CreationDate, serverClientSpan, optioninfo.ExpiryMonths, optioninfo.ExpiryWeek, optioninfo.ExpiryDays, optioninfo.ExpiryTime.Hour, optioninfo.ExpiryTime.Minute, optioninfo.TradingPeriod);
        }

        /// <summary>
        /// Gets the expiry list.
        /// </summary>
        /// <param name="optioninfo">The optioninfo.</param>
        /// <returns></returns>
        public List<DateTime> GetExpiryList(OptionInfo optioninfo)
        {
            return GetDateList(optioninfo.CreationDate, optioninfo.ExpiryMonths, optioninfo.ExpiryWeek, optioninfo.ExpiryDays, optioninfo.ExpiryTime.Hour, optioninfo.ExpiryTime.Minute, optioninfo.TradingPeriod);
        }

        /// <summary>
        /// Gets the expiry list.
        /// </summary>
        /// <param name="beginDatetime">The begin datetime.</param>
        /// <param name="monthNum">The month num.</param>
        /// <param name="weekNum">The week num.</param>
        /// <param name="weekDay">The week day.</param>
        /// <param name="hour">The hour.</param>
        /// <param name="minute">The minute.</param>
        /// <param name="tradingPeriod">The trading period.</param>
        /// <returns></returns>
        public List<DateTime> GetExpiryList(DateTime beginDatetime, int monthNum, int weekNum, int weekDay, int hour, int minute, int tradingPeriod)
        {
            return GetDateList(beginDatetime, monthNum, weekNum, weekDay, hour, minute, tradingPeriod);
        }

        /// <summary>
        /// Gets the precision.
        /// </summary>
        /// <param name="tick">The tick.</param>
        /// <returns></returns>
        public static int GetPrecision(double tick)
        {
            if (tick.ToString().Split('.').Length == 1)
                return 0;
            else
                return tick.ToString().Split('.')[1].Length;
        }

        #endregion
        #endregion

        #region private
        private void FillOptionData(OptionType optionType, double strike, double time, double volatility, double gamma, OptionData optionData, double vega)
        {
            optionData.Ask = GetAsk(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor);
            optionData.Bid = GetBid(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor);
            optionData.Volatility = Math.Round(volatility, 4);
            optionData.OptionType = optionType;
            optionData.High = GetAsk(optionType, _option.High, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor);
            optionData.Low = GetBid(optionType, _option.Low, strike, _option.InterestRate, time, volatility, _option.ForeignRate, _option.SpreadFactor);
            optionData.Delta = Getdelta(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate);
            optionData.Gamma = gamma;
            optionData.Theta = GetTheta(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate);
            optionData.Strike = strike;
            optionData.Rho = GetRho(optionType, _atm, strike, _option.InterestRate, time, volatility, _option.ForeignRate);
            optionData.Expiry = _option.Expiry;
            optionData.Vega = vega;
        }

        private static double GetVolatility(double atm, double Slope, double Curve, double strike, double atmPrice, double cruxFactor)
        {
            double e = Math.Abs(strike / atmPrice / cruxFactor - 1);
            double f = e * e;
            return atm + Slope * e + Curve * f;
        }

        private static double GetBid(OptionType optionType, double atmPrice, double strike, double rate, double time, double volatility, double yield, double spread)
        {
            double intrinsic = 0;
            double bid = 0;
            double premium = GetPremium(optionType, atmPrice, strike, rate, time, volatility, yield);
            if (optionType == OptionType.Call)
            {
                intrinsic = atmPrice - strike;
            }
            else if (optionType == OptionType.Put)
            {
                intrinsic = strike - atmPrice;
            }
            if (intrinsic < 0)
                intrinsic = 0;

            bid = (premium - intrinsic) * (1 - spread / 200) + intrinsic;
            return bid;
        }

        private static double GetAsk(OptionType optionType, double atmPrice, double strike, double rate, double time, double volatility, double yield, double spread)
        {
            double intrinsic = 0;
            double ask = 0;
            double premium = GetPremium(optionType, atmPrice, strike, rate, time, volatility, yield);
            double bid = GetBid(optionType, atmPrice, strike, rate, time, volatility, yield, spread) + 0.0001;

            if (optionType == OptionType.Call)
            {
                intrinsic = atmPrice - strike;
            }
            else if (optionType == OptionType.Put)
            {
                intrinsic = strike - atmPrice;
            }

            if (intrinsic < 0)
                intrinsic = 0;

            ask = (premium - intrinsic) * (1 + spread / 200) + intrinsic;

            if (ask < bid) ask = bid;

            return ask;
        }

        //delta for Call and Put
        private double Getdelta(OptionType optionType, double atmPrice, double strike, double rate, double time, double volatility, double yield)
        {
            double d1 = 0.0;
            double delta = 0.0;
            if (time < 0)
            {
                time = 0.00000000001;
            }
            double timew = time / 365D;

            d1 = (Math.Log(atmPrice / strike) + (rate - yield + volatility * volatility / 2.0) * timew) / (volatility * Math.Sqrt(timew));

            if (optionType == OptionType.Call)
            {
                delta = Math.Exp(-yield * timew) * CND(d1);
            }
            else if (optionType == OptionType.Put)
            {
                delta = -Math.Exp(-yield * timew) * CND(-d1);
            }

            return delta;
        }

        //gamma is the same for Put and Call
        private double GetGamma(double atmPrice, double strike, double rate, double time, double volatility, double yield)
        {
            double d1 = 0.0;
            if (time < 0)
            {
                time = 0.00000000001;
            }
            double timew = time / 365D;

            d1 = (Math.Log(atmPrice / strike) + (rate - yield + volatility * volatility / 2.0) * timew) / (volatility * Math.Sqrt(timew));

            return Math.Exp(-yield * timew) * phi(d1) / (atmPrice * volatility * Math.Sqrt(timew));
        }

        //vega is the same for Put and Call
        private double GetVega(double atmPrice, double strike, double rate, double time, double volatility, double yield)
        {
            double d1 = 0.0;
            if (time < 0)
            {
                time = 0.00000000001;
            }
            double timew = time / 365D;

            d1 = (Math.Log(atmPrice / strike) + (rate - yield + volatility * volatility / 2.0) * timew) / (volatility * Math.Sqrt(timew));
            return atmPrice * Math.Exp(-yield * timew) * phi(d1) * Math.Sqrt(timew);
        }

        //theta for Call and Put
        private double GetTheta(OptionType optionType, double atmPrice, double strike, double rate, double time, double volatility, double yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double theta = 0.0;
            if (time < 0)
            {
                time = 0.00000000001;
            }
            double timew = time / 365D;

            d1 = (Math.Log(atmPrice / strike) + (rate - yield + volatility * volatility / 2.0) * timew) / (volatility * Math.Sqrt(timew));
            d2 = d1 - volatility * Math.Sqrt(timew);

            if (optionType == OptionType.Call)
            {
                theta = -Math.Exp(-yield * timew) * (atmPrice * phi(d1) * volatility / (2 * Math.Sqrt(timew))) - rate * strike * Math.Exp(-rate * timew) * CND(d2) + yield * atmPrice * Math.Exp(-yield * timew) * CND(d1);
            }
            else if (optionType == OptionType.Put)
            {
                theta = -Math.Exp(-yield * timew) * (atmPrice * phi(d1) * volatility / (2 * Math.Sqrt(timew))) + rate * strike * Math.Exp(-rate * timew) * CND(-d2) - yield * atmPrice * Math.Exp(-yield * timew) * CND(-d1);
            }

            return theta;
        }

        //rho for Call and Put
        private double GetRho(OptionType optionType, double atmPrice, double strike, double rate, double time, double volatility, double yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double rho = 0.0;
            if (time < 0)
            {
                time = 0.00000000001;
            }
            double timew = time / 365D;

            d1 = (Math.Log(atmPrice / strike) + (rate - yield + volatility * volatility / 2.0) * timew) / (volatility * Math.Sqrt(timew));
            d2 = d1 - volatility * Math.Sqrt(timew);

            if (optionType == OptionType.Call)
            {
                rho = strike * timew * Math.Exp(-rate * timew) * CND(d2);
            }
            else if (optionType == OptionType.Put)
            {
                rho = -strike * timew * Math.Exp(-rate * timew) * CND(-d2);
            }

            return rho;
        }

        private static double GetPremium(OptionType optionType, double atmPrice, double strike, double rate, double time, double volatility, double yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double dBlackScholes = 0.0;
            if (time < 0)
            {
                time = 0.00000000001;
            }
            double timew = time / 365D;

            d1 = (Math.Log(atmPrice / strike) + (rate - yield + volatility * volatility / 2.0) * timew) / (volatility * Math.Sqrt(timew));
            d2 = d1 - volatility * Math.Sqrt(timew);

            if (optionType == OptionType.Call)
            {
                dBlackScholes = atmPrice * Math.Exp(-yield * timew) * CND(d1) - strike * Math.Exp(-rate * timew) * CND(d2);
            }
            else if (optionType == OptionType.Put)
            {
                dBlackScholes = strike * Math.Exp(-rate * timew) * CND(-d2) - atmPrice * Math.Exp(-yield * timew) * CND(-d1);
            }

            return dBlackScholes;
        }

        //function phi
        private double phi(double x)
        {
            double phi = 0.0;

            phi = Math.Exp(-x * x / 2) / Math.Sqrt(2 * Math.PI);
            return phi;
        }

        private static double CND(double X)
        {
            double L = 0.0;
            double K = 0.0;
            double dCND = 0.0;
            const double a1 = 0.31938153;
            const double a2 = -0.356563782;
            const double a3 = 1.781477937;
            const double a4 = -1.821255978;
            const double a5 = 1.330274429;
            L = Math.Abs(X);
            K = 1.0 / (1.0 + 0.2316419 * L);
            dCND = 1.0 - 1.0 / Math.Sqrt(2 * Convert.ToDouble(Math.PI.ToString())) *
                Math.Exp(-L * L / 2.0) * (a1 * K + a2 * K * K + a3 * Math.Pow(K, 3.0) +
                a4 * Math.Pow(K, 4.0) + a5 * Math.Pow(K, 5.0));

            if (X < 0)
            {
                return 1.0 - dCND;
            }
            else
            {
                return dCND;
            }
        }

        /// <summary>
        /// get the dateList
        /// </summary>
        /// <param name="beginDatetime">begin dateTime</param>
        /// <param name="serverclientSpan">The serverclient span.</param>
        /// <param name="month_Num">the quntity of month you want to show</param>
        /// <param name="weekNo">he week No. from 1 to 4</param>
        /// <param name="weekDay">the day of week from 1 to 7</param>
        /// <param name="deHour">the end hour</param>
        /// <param name="deMinute">the end Minute</param>
        /// <param name="tradingPeriod">The trading period.</param>
        /// <returns></returns>
        private List<DateTime> GetDateList(DateTime beginDatetime, TimeSpan serverclientSpan, int month_Num, int weekNo, int weekDay, int deHour, int deMinute, int tradingPeriod)
        {
            var comparetime = DateTime.Now + serverclientSpan;
            return GetDateList(beginDatetime, comparetime, month_Num, weekNo, weekDay, deHour, deMinute, tradingPeriod);
        }

        /// <summary>
        /// get the dateList
        /// </summary>
        /// <param name="beginDatetime">begin dateTime</param>
        /// <param name="compareTime">The compare time.</param>
        /// <param name="month_Num">the quntity of month you want to show</param>
        /// <param name="weekNo">he week No. from 1 to 4</param>
        /// <param name="weekDay">the day of week from 1 to 7</param>
        /// <param name="deHour">the end hour</param>
        /// <param name="deMinute">the end Minute</param>
        /// <param name="tradingPeriod">The trading period.</param>
        /// <returns></returns>
        private List<DateTime> GetDateList(DateTime beginDatetime, DateTime compareTime, int month_Num, int weekNo, int weekDay, int deHour, int deMinute, int tradingPeriod)
        {
            if (tradingPeriod == -1)
            {
                beginDatetime = compareTime;
            }
            else if (tradingPeriod < month_Num)
            {
                month_Num = tradingPeriod;
            }

            int year = Convert.ToInt32(beginDatetime.ToString("yyyy"));   //the begin year
            int month = Convert.ToInt32(beginDatetime.Month);   //the begin month
            int day = Convert.ToInt32(beginDatetime.Day);   //the begin day
            int minute = Convert.ToInt32(beginDatetime.Minute);   //the begin minute
            int hour = Convert.ToInt32(beginDatetime.Hour);   //the begin hour
            int weekDayOfBeginMonth = Convert.ToInt32((new DateTime(year, month, 1)).DayOfWeek);
            weekDayOfBeginMonth = weekDayOfBeginMonth == 0 ? 7 : weekDayOfBeginMonth;
            int dayOfMonth = 0;   //the day of the month that meet the requirments
            List<DateTime> dateTimeList = new List<DateTime>();   //dateList to return
            DateTime datetime;   //datetime to store the datetime temporarily
            int yearForlist = 0;   //store the year temporarily
            int monthForList = 0;   //store the month temporarily
            int dayForList = 0;   //store the day temporarily
            int beginweekDay = 0;   //store the begin day 's day of week temporarily
            dayOfMonth = GetDay(weekDayOfBeginMonth, weekDay, weekNo);

            //not Overdue
            if (day < dayOfMonth || (day == dayOfMonth && hour < deHour) || (day == dayOfMonth && hour == deHour && minute < deMinute))
            {
                datetime = new DateTime(year, month, dayOfMonth, deHour, deMinute, 0);
                if (DateTime.Compare(datetime, compareTime) > 0)
                    dateTimeList.Add(datetime);

                for (int i = 1; i < month_Num; i++)
                {
                    yearForlist = year + (month + i - 1) / 12;
                    monthForList = (month + i) % 12 == 0 ? 12 : (month + i) % 12;
                    beginweekDay = Convert.ToInt32((new DateTime(yearForlist, monthForList, 1)).DayOfWeek);
                    beginweekDay = beginweekDay == 0 ? 7 : beginweekDay;
                    dayForList = GetDay(beginweekDay, weekDay, weekNo);
                    datetime = new DateTime(yearForlist, monthForList, dayForList, deHour, deMinute, 0);
                    if (DateTime.Compare(datetime, compareTime) > 0)
                        dateTimeList.Add(datetime);
                }
            }
            else   //Overdue
            {
                for (int i = 1; i < month_Num + 1; i++)
                {
                    yearForlist = year + (month + i - 1) / 12;
                    monthForList = (month + i) % 12 == 0 ? 12 : (month + i) % 12;
                    beginweekDay = Convert.ToInt32((new DateTime(yearForlist, monthForList, 1)).DayOfWeek);
                    beginweekDay = beginweekDay == 0 ? 7 : beginweekDay;
                    dayForList = GetDay(beginweekDay, weekDay, weekNo);
                    datetime = new DateTime(yearForlist, monthForList, dayForList, deHour, deMinute, 0);
                    if (DateTime.Compare(datetime, compareTime) > 0)
                        dateTimeList.Add(datetime);
                }

            }
            return dateTimeList;
        }

        /// <summary>
        /// get the dateList
        /// </summary>
        /// <param name="beginDatetime">begin dateTime</param>
        /// <param name="month_Num">the quntity of month you want to show </param>
        /// <param name="weekNo">he week No. from 1 to 4</param>
        /// <param name="weekDay">the day of week from 1 to 7</param>
        /// <param name="deHour">the end hour</param>
        /// <param name="deMinute">the end Minute</param>
        private List<DateTime> GetDateList(DateTime beginDatetime, int month_Num, int weekNo, int weekDay, int deHour, int deMinute, int tradingPeriod)
        {
            if (tradingPeriod == -1)
            {
                beginDatetime = DateTime.Now;
            }
            else if (tradingPeriod < month_Num)
            {
                month_Num = tradingPeriod;
            }

            int year = Convert.ToInt32(beginDatetime.ToString("yyyy"));   //the begin year
            int month = Convert.ToInt32(beginDatetime.Month);   //the begin month
            int day = Convert.ToInt32(beginDatetime.Day);   //the begin day
            int minute = Convert.ToInt32(beginDatetime.Minute);   //the begin minute
            int hour = Convert.ToInt32(beginDatetime.Hour);   //the begin hour
            int weekDayOfBeginMonth = Convert.ToInt32((new DateTime(year, month, 1)).DayOfWeek);
            weekDayOfBeginMonth = weekDayOfBeginMonth == 0 ? 7 : weekDayOfBeginMonth;
            int dayOfMonth = 0;   //the day of the month that meet the requirments
            List<DateTime> dateTimeList = new List<DateTime>();   //dateList to return
            DateTime datetime;   //datetime to store the datetime temporarily
            int yearForlist = 0;   //store the year temporarily
            int monthForList = 0;   //store the month temporarily
            int dayForList = 0;   //store the day temporarily
            int beginweekDay = 0;   //store the begin day 's day of week temporarily
            dayOfMonth = GetDay(weekDayOfBeginMonth, weekDay, weekNo);
            //not Overdue
            if (day < dayOfMonth || (day == dayOfMonth && hour < deHour) || (day == dayOfMonth && hour == deHour && minute < deMinute))
            {
                datetime = new DateTime(year, month, dayOfMonth, deHour, deMinute, 0);
                if (DateTime.Compare(datetime, DateTime.Now) > 0)
                    dateTimeList.Add(datetime);

                for (int i = 1; i < month_Num; i++)
                {
                    yearForlist = year + (month + i - 1) / 12;
                    monthForList = (month + i) % 12 == 0 ? 12 : (month + i) % 12;
                    beginweekDay = Convert.ToInt32((new DateTime(yearForlist, monthForList, 1)).DayOfWeek);
                    beginweekDay = beginweekDay == 0 ? 7 : beginweekDay;
                    dayForList = GetDay(beginweekDay, weekDay, weekNo);
                    datetime = new DateTime(yearForlist, monthForList, dayForList, deHour, deMinute, 0);
                    if (DateTime.Compare(datetime, DateTime.Now) > 0)
                        dateTimeList.Add(datetime);
                }
            }
            else   //Overdue
            {
                for (int i = 1; i < month_Num + 1; i++)
                {
                    yearForlist = year + (month + i - 1) / 12;
                    monthForList = (month + i) % 12 == 0 ? 12 : (month + i) % 12;
                    beginweekDay = Convert.ToInt32((new DateTime(yearForlist, monthForList, 1)).DayOfWeek);
                    beginweekDay = beginweekDay == 0 ? 7 : beginweekDay;
                    dayForList = GetDay(beginweekDay, weekDay, weekNo);
                    datetime = new DateTime(yearForlist, monthForList, dayForList, deHour, deMinute, 0);
                    if (DateTime.Compare(datetime, DateTime.Now) > 0)
                        dateTimeList.Add(datetime);
                }

            }
            return dateTimeList;
        }
        /// <summary>
        /// get the day of the month
        /// </summary>
        private int GetDay(int beginweekDay, int weekDay, int weekNo)
        {
            int day = 0;

            if (beginweekDay <= weekDay)
            {
                day = 7 * weekNo - beginweekDay + weekDay - 6;
            }
            else
            {
                day = 7 * weekNo - beginweekDay + weekDay + 1;
            }
            return day;
        }
        #endregion
    }

    /// <summary>
    /// 
    /// </summary>
    public static class OptionMappingHelper
    {
        /// <summary>
        /// Gets the option settings.
        /// </summary>
        /// <param name="option">The option.</param>
        /// <param name="expiry">The expiry.</param>
        /// <returns></returns>
        public static OptionSettings GetOptionSettings(OptionInfo option, DateTime expiry)
        {
            return new OptionSettings
            {
                AtmVolatility = option.AtmVolatility,
                CruxFactor = option.CruxFactor,
                Expiry = expiry,
                ForeignRate = option.ForeignRate,
                InterestRate = option.InterestRate,
                LowerCurve = option.LowerCurve,
                LowerSlope = option.LowerSlope,
                SpreadFactor = option.SpreadFactor,
                StrikeIncrement = option.StrikeIncrement,
                tradableCount = option.TradableCount,
                UpperCurve = option.UpperCurve,
                UpperSlope = option.UpperSlope
            };
        }
    }
